package Gamefield;

import java.util.ArrayList;
import java.util.Hashtable;
import java.util.List;

import Pieces.MoveDirections;
import Pieces.Piece;
import Pieces.PieceFactory;
import Pieces.PieceMovement;
import Pieces.PiecePlayerEnum;

public class Gamefield implements Cloneable{
	private final int _rows = 5;
	private final int _cols = 4;
	private Tray _whiteTray = new Tray();
	private Tray _blackTray = new Tray();
	private Field[][] _field = new Field[_rows][_cols];
	private Hashtable<String,Integer> _heuristicHash = new Hashtable<String,Integer>();
	
	public Field[][] getField() {
		return _field;
	}

	public void setField(Field[][] field) {
		this._field = field;
	}

	public Field[][] GetStandardPosition(){
		Field[][] standardField = new Field[_rows][_cols];
		Piece noPiece = null;
		
		for (int row = 1; row < _rows; row++) {
			for (int col = 1; col < _cols; col++) {
				switch (row){
				case 1:	
					if(col == 1){
						standardField[row][col] = new Field(PieceFactory.createRook(PiecePlayerEnum.Black, row, col));
					} else if (col == 2){
						standardField[row][col] = new Field(PieceFactory.createKing(PiecePlayerEnum.Black, row, col));
					} else if (col == 3){
						standardField[row][col] = new Field(PieceFactory.createBishop(PiecePlayerEnum.Black, row, col));
					}
					break;
				case 2:
					if(col == 2){
						standardField[row][col] = new Field(PieceFactory.createPawn(PiecePlayerEnum.Black, row, col));
					} else {
						standardField[row][col] = new Field(noPiece);
					}
					break;
				case 3:
					if(col == 2){
						standardField[row][col] = new Field(PieceFactory.createPawn(PiecePlayerEnum.White, row, col));
					} else {
						standardField[row][col] = new Field(noPiece);
					}
					break;
				case 4:
					if(col == 1){
						standardField[row][col] = new Field(PieceFactory.createBishop(PiecePlayerEnum.White, row, col));
					} else if (col == 2){
						standardField[row][col] = new Field(PieceFactory.createKing(PiecePlayerEnum.White, row, col));
					} else if (col == 3){
						standardField[row][col] = new Field(PieceFactory.createRook(PiecePlayerEnum.White, row, col));
					}
					break;
				}			
			}
		}
		return standardField;
	}

	public boolean IsFinishPos() {
		return false;
	}

	public String GetFinishPos() {
		return "";
	}

	public Tray getTray(PiecePlayerEnum player) {
		if(player == PiecePlayerEnum.White){
			return _whiteTray;
		} else {
			return _blackTray;
		}
	}

	
	public ArrayList<Piece> getAllPieces(PiecePlayerEnum color) {
		ArrayList<Piece> allPiecesInNotation = new ArrayList<Piece>();
		for (int row = 1; row < _rows; row++) {
			for (int col = 1; col < _cols; col++) {
				if(! _field[row][col].equals(" ")){
					if(_field[row][col].getPiece() != null){
						if (color == _field[row][col].getPiece().getPlayerColor()){
							allPiecesInNotation.add(_field[row][col].getPiece());	
						}
					}
				}
			}
		}
		
		return allPiecesInNotation;
	}
	
	private static char getColFromInt(int number){
		char str;
		if (number == 1){
			str = 'A'; 
		} else if(number == 2){
			str = 'B';
		} else if(number == 3){
			str = 'C'; 
		} else {
			str = '!';
		}
		
		return str;
	}

	public static Gamefield executeNextMove(Gamefield gamefield, PieceMovement nextMove) {
		Field[][] field = gamefield.getField();
		
		if(nextMove.getPosFrom().isDrop()){
			Tray t = gamefield.getTray(nextMove.getPlayer().getPlayerCol());
			Piece p = t.usePiece(nextMove.getFig());
			//shouldn't happen
			if(p != null){
				field[nextMove.getPosTo().getRow()][nextMove.getPosTo().getCol()] = new Field(p);				
			}
		} else if(nextMove.isCapture()){
			Field tempField = field[nextMove.getPosTo().getRow()][nextMove.getPosTo().getCol()];
			field[nextMove.getPosTo().getRow()][nextMove.getPosTo().getCol()] = field[nextMove.getPosFrom().getRow()][nextMove.getPosFrom().getCol()];
			field[nextMove.getPosFrom().getRow()][nextMove.getPosFrom().getCol()] = new Field();
			
			// Generals are promoted back
			if(tempField.getPiece().getPiecename()=='G' || tempField.getPiece().getPiecename()=='g'){
				tempField.setPiece(PieceFactory.createPawn(nextMove.getPlayer().getPlayerCol(),
									nextMove.getPosTo().getRow(),
									nextMove.getPosTo().getCol()));
			}
			
			tempField.getPiece().capturePiece(); // Capture Piece
			tempField.getPiece().changeDirections();
			
			if(nextMove.getPlayer().getPlayerCol() == PiecePlayerEnum.White){
				gamefield.addWhiteTray(tempField.getPiece());
			} else {
				gamefield.addBlackTray(tempField.getPiece());
			}
		} else {
			Field tempField = field[nextMove.getPosTo().getRow()][nextMove.getPosTo().getCol()];
			field[nextMove.getPosTo().getRow()][nextMove.getPosTo().getCol()] = field[nextMove.getPosFrom().getRow()][nextMove.getPosFrom().getCol()];
			field[nextMove.getPosFrom().getRow()][nextMove.getPosFrom().getCol()] = tempField;
		}
		
		if(nextMove.isPromotion()){
			Field promotionField = field[nextMove.getPosTo().getRow()][nextMove.getPosTo().getCol()];
			if((promotionField.getPiece().getPiecename()=='P')||
					(promotionField.getPiece().getPiecename()=='p')){
						promotionField.setPiece(PieceFactory.createGeneral(nextMove.getPlayer().getPlayerCol(),
							nextMove.getPosTo().getRow(),
							nextMove.getPosTo().getCol()));
			}
		}
		gamefield.setField(field);
		return gamefield;
	}

	private void addBlackTray(Piece piece) {
		_blackTray.add(piece);
	}

	private void addWhiteTray(Piece piece) {
		_whiteTray.add(piece);
	}

	public static boolean KingIsInCheck(Gamefield gamefield, Player playerInCheck) {
		FieldPosition fp = Gamefield.findKing(gamefield,playerInCheck);
		Field[][] tempfield = gamefield.getField();
		boolean check = false;
		
		if(fp.getCol() == -1 && fp.getRow() == -1){
			System.out.println("ERROR in 'KING IS IN CHECK'");
		}
		
		if(tempfield[fp.getRow()][fp.getCol()].getPiece() != null){
			Field[] neighbourfields = getNeighbourFields(tempfield,fp.getRow(),fp.getCol());
			for(int i=0;i<neighbourfields.length;i++){
				if(neighbourfields[i]!=null){
					MoveDirections md = MoveDirections.NO;
					
					switch(i){
						case 0:  md = MoveDirections.NO;break;
						case 1:  md = MoveDirections.NE;break;
						case 2:  md = MoveDirections.EA;break;
						case 3:  md = MoveDirections.SE;break;
						case 4:  md = MoveDirections.SO;break;
						case 5:  md = MoveDirections.SW;break;
						case 6:  md = MoveDirections.WE;break;
						case 7:  md = MoveDirections.NW;break;
					}
					
					Piece p = neighbourfields[i].getPiece();
					if(p != null){
						if(p.getPlayerColor() != playerInCheck.getPlayerCol()){
							for (MoveDirections md_opponent : neighbourfields[i].getPiece().getPossibleMoves()) {
								// if there is an attack (piece could move to where you are facing), it's check
								if(MoveDirections.getOtherDirection(md_opponent) == md){
									check = true;
								}
							}
						}
					}
				}
			}
		}
		
		return check;
	}
	
	private static Field[] getNeighbourFields(Field[][] tempfield, int rowPos, int colPos) {
		Field[] neighbours = new Field[8]; // clockwise Movedirections
		
		for(int i=0;i<neighbours.length;i++){
			if(rowPos != 1){
				neighbours[0]=tempfield[rowPos-1][colPos];			
			} 
			if(rowPos != 1 && colPos != 3){
				neighbours[1]=tempfield[rowPos-1][colPos+1];			
			} 
			if(colPos != 3){
				neighbours[2]=tempfield[rowPos][colPos+1];			
			} 
			if(rowPos != 4 && colPos != 3){
				neighbours[3]=tempfield[rowPos+1][colPos+1];			
			} 
			if(rowPos != 4){
				neighbours[4]=tempfield[rowPos+1][colPos];			
			}
			if(rowPos != 4 && colPos != 1){
				neighbours[5]=tempfield[rowPos+1][colPos-1];			
			} 
			if(colPos != 1){
				neighbours[6]=tempfield[rowPos][colPos-1];			
			} 
			if(rowPos != 1 && colPos != 1){
				neighbours[7]=tempfield[rowPos-1][colPos-1];			
			} 
		}		
		return neighbours;
	}

	public static FieldPosition findKing(Gamefield gamefield,Player player){
		FieldPosition fp = new FieldPosition(-1, -1);
		Field[][] tempfield = gamefield.getField();
		for (int row = 1; row < gamefield._rows; row++) {
			for (int col = 1; col < gamefield._cols; col++) {
				Piece p = tempfield[row][col].getPiece();
				if(p != null){
					if(p.isKing(player)){
						fp.setRow(row);
						fp.setCol(col);
					}
				}
			}
		}
		
		return fp;
	}
	
	public Gamefield(Gamefield copy){
		this._blackTray = new Tray(copy.getTray(PiecePlayerEnum.Black));
		this._whiteTray = new Tray(copy.getTray(PiecePlayerEnum.White));
		
		for (int row = 1; row < _rows; row++) {
			for (int col = 1; col < _cols; col++) {
				this._field[row][col] = new Field(copy.getField()[row][col]);
			}
		}
	}
	
	public Gamefield(){
		this._heuristicHash.put("0", 0);
		this._heuristicHash.put("P", 1);
		this._heuristicHash.put("B", 3);
		this._heuristicHash.put("R", 3);
		this._heuristicHash.put("G", 5);
		this._heuristicHash.put("K", 20);
	}

	public static MoveDirections getDirection(FieldPosition posFrom,
			FieldPosition posTo) {
		//TODO 
		MoveDirections md = MoveDirections.DR; // Drops already set ... cannot happen
		if(posFrom.getRow() == posTo.getRow() + 1 && posFrom.getCol() == posTo.getCol()){
			md = MoveDirections.NO;
		} else if(posFrom.getRow() == posTo.getRow() + 1 && posFrom.getCol() == posTo.getCol() - 1){
			md = MoveDirections.NW;
		} else if(posFrom.getRow() == posTo.getRow()+1  && posFrom.getCol() == posTo.getCol() + 1){
			md = MoveDirections.NE;
		} else if(posFrom.getRow() == posTo.getRow() && posFrom.getCol() == posTo.getCol() -1){
			md = MoveDirections.WE;
		} else if(posFrom.getRow() == posTo.getRow() && posFrom.getCol() == posTo.getCol() + 1){
			md = MoveDirections.EA;
		} else if(posFrom.getRow() == posTo.getRow() - 1 && posFrom.getCol() == posTo.getCol() - 1){
			md = MoveDirections.SW;
		} else if(posFrom.getRow() == posTo.getRow() - 1 && posFrom.getCol() == posTo.getCol()){
			md = MoveDirections.SO;
		} else if(posFrom.getRow() == posTo.getRow() - 1 && posFrom.getCol() == posTo.getCol() + 1){
			md = MoveDirections.SE;
		}
		return md;
	}

	public boolean targetFieldInGamefield(FieldPosition posTo) {
		boolean check = true;
		int rows = _field.length -1;
		int cols = _field[1].length -1; 
		
		if (posTo.getRow() < 0 || posTo.getRow() > rows){
			check = false;
		}
		if (posTo.getCol() < 0 || posTo.getCol() > cols){
			check = false;
		}
		
		return check;
	}

	public List<PieceMovement> getAllPossibleMoves(Player _curPlayer) {
		ArrayList<PieceMovement> allMoves = new ArrayList<PieceMovement>();
		ArrayList<Piece> allPieces = getAllPieces(_curPlayer.getPlayerCol());
		
		//TODO
		
		for (Piece piece : allPieces) {
			allMoves.addAll(piece.getValidMoves());
		}
		return allMoves;
	}
}
